#ifndef DEVICE_H_
#define DEVICE_H_

#include <pthread.h>
#include <fstream>
#include <queue>
#include <map>
#include <vector>

#include "common_defs.h"
#include "Request.h"

class Device
{

public:

	Device();

	~Device();

	// should be called only once prior to any method call
	result_t create_device();

	result_t init_device( char *filename );

	result_t device_stop();

	result_t add_request( char* buffer, uint length, uint *id );

	// block the thread until the request with the given id is served
	result_t wait_request(uint id);

	result_t was_writen(uint id);

	/*
	 * release all the allocated resources of the device
	 * in case force_release is true, the releasing operation will try release as much
	 * as possible resources so it will ignore system call errors.
	 */
	result_t device_release_resources(bool force_release = false);

private:

	/*
	 *  get the next request and remove it only from the heap of waiting requests
	 *  in case the queue is empty it will be blocked until a signal of a new request
	 *  will be received
	 */
	result_t pop_next_request(Request* &request);

	// remove the request that matches the given ID from all the databases
	result_t remove_request( uint id );

	// return the next available request's ID
	uint next_request_id();

	// make the process wait until there is a request that ready to be served
	result_t wait_queue_not_empty();

	result_t push_to_reserved_heap(uint id);

	// check if the closing request was received
	result_t is_on_closing(bool &on_closing);

	/*
	 * make the execution function of the daemon thread a friend of the class so it
	 * can access private fields of the class
	 */
	friend void* daemon_exec_func(void *thread_args);


	std::ofstream _outfile;			// output file
	pthread_mutex_t _context_lock;	// lock of the descriptor that protects the databases
	bool _initialized;				// indicate if the device is initialized
	bool _on_closing;				/*< indicator that indicate if the device
										performing a closing operation 				>*/

	pthread_t _daemon_thread;		// handler of the thread that serve the requests
	bool _daemon_thread_done_work;	/*< indicate if the daemon thread may modify
										the database so we can't release the resources >*/

	std::map< uint,Request*> _requests;
	std::queue<Request*> _queue;	// queue of the waiting requests
	pthread_cond_t _queue_not_empty;// indicate if the queue has waiting requests
	bool _cond_var_created;

	uint _next_id_generate;
	std::vector<uint> _served_requests; // heap of IDs that can be reused

};


#endif /* DEVICE_H_ */
